Travail sur des données ouvertes disponibles au QC

https://www.donneesquebec.ca/recherche/dataset/vmtl-actes-criminels, Liste des actes criminels enregistrés par le Service de police de la Ville de Montréal (SPVM).

Liste des actes criminels enregistrés par le Service de police de la Ville de Montréal (SPVM). Criminalité - 2015 à aujourd’hui ## Importer données à partir du WEB

Il faut d’abord importer les données et s’assurer du format de celle-ci. Un simple head() est suffisant pour nos besoins.

data_URL <- "https://data.montreal.ca/dataset/5829b5b0-ea6f-476f-be94-bc2b8797769a/resource/c6f482bf-bf0f-4960-8b2f-9982c211addd/download/interventionscitoyendo.csv"
dat <- read.csv(file = data_URL)
head(dat)
##                        CATEGORIE       DATE QUART PDQ        X       Y
## 1 Vol de v\xe9hicule \xe0 moteur 2018-09-13  jour  30 294904.2 5047549
## 2 Vol de v\xe9hicule \xe0 moteur 2018-04-30  jour  30 294904.2 5047549
## 3 Vol de v\xe9hicule \xe0 moteur 2018-09-01  nuit   7 290274.6 5042150
## 4                      M\xe9fait 2017-07-21  jour  21      0.0       0
## 5                      M\xe9fait 2017-07-29  jour  12      0.0       0
## 6 Vol de v\xe9hicule \xe0 moteur 2017-07-30  nuit  21      0.0       0
##   LONGITUDE LATITUDE
## 1 -73.62678 45.56778
## 2 -73.62678 45.56778
## 3 -73.68593 45.51912
## 4 -76.23729  0.00000
## 5 -76.23729  0.00000
## 6 -76.23729  0.00000

premier problème, les accents sont bizarres… que faire? Dans les informations complémentaires sur la page de téléchargement des données, il est inscrit: > Encodage de caractères > Le fichier CSV utilise l’encodage de caractère ISO-8859-1 (latin1)

Il doit bien y avoir une façon de lire le fichier en spécifiant un encodage particulier.

`?`(read.csv)

Dans l’aide, on y retrouve un argument ou deux arguments qui nous mèneront peut-être au résultat espérer, soit fileEncoding = "" et encoding = "unknown",

read.table(file, header = FALSE, sep = "", quote = "\"'",
           dec = ".", numerals = c("allow.loss", "warn.loss", "no.loss"),
           row.names, col.names, as.is = !stringsAsFactors,
           na.strings = "NA", colClasses = NA, nrows = -1,
           skip = 0, check.names = TRUE, fill = !blank.lines.skip,
           strip.white = FALSE, blank.lines.skip = TRUE,
           comment.char = "#",
           allowEscapes = FALSE, flush = FALSE,
           stringsAsFactors = default.stringsAsFactors(),
           fileEncoding = "", encoding = "unknown", text, skipNul = FALSE)

fileEncoding: character string: if non-empty declares the encoding used on a file (not a connection) so the character data can be re-encoded. See the ‘Encoding’ section of the help for file, the ‘R Data Import/Export Manual’ and ‘Note’.

encoding: encoding to be assumed for input strings. It is used to mark character strings as known to be in Latin-1 or UTF-8 (see Encoding): it is not used to re-encode the input, but allows R to handle encoded strings in their native encoding (if one of those two). See ‘Value’ and ‘Note’.

En lisant les deux options, il est difficile de savoir quel argument nous devons choisir. Heureusement, la section Note précise que les deux peuvent être utilisé selon le format d’encodage de l’input: > Note > There are two approaches for reading input that is not in the local encoding. If the input is known to be UTF-8 or Latin1, use the encoding argument to declare that. If the input is in some other encoding, then it may be translated on input. The fileEncoding argument achieves this by setting up a connection to do the re-encoding into the current locale. Note that on Windows or other systems not running in a UTF-8 locale, this may not be possible.

À ce stade de confusion, nous pouvons essayer les deux options et observer les différences..

head(read.csv(file = data_URL, encoding = "Latin1"))
##                        CATEGORIE       DATE QUART PDQ        X       Y
## 1 Vol de v\xe9hicule \xe0 moteur 2018-09-13  jour  30 294904.2 5047549
## 2 Vol de v\xe9hicule \xe0 moteur 2018-04-30  jour  30 294904.2 5047549
## 3 Vol de v\xe9hicule \xe0 moteur 2018-09-01  nuit   7 290274.6 5042150
## 4                      M\xe9fait 2017-07-21  jour  21      0.0       0
## 5                      M\xe9fait 2017-07-29  jour  12      0.0       0
## 6 Vol de v\xe9hicule \xe0 moteur 2017-07-30  nuit  21      0.0       0
##   LONGITUDE LATITUDE
## 1 -73.62678 45.56778
## 2 -73.62678 45.56778
## 3 -73.68593 45.51912
## 4 -76.23729  0.00000
## 5 -76.23729  0.00000
## 6 -76.23729  0.00000
head(read.csv(file = data_URL, fileEncoding = "Latin1"))
##                  CATEGORIE       DATE QUART PDQ        X       Y LONGITUDE
## 1 Vol de véhicule à moteur 2018-09-13  jour  30 294904.2 5047549 -73.62678
## 2 Vol de véhicule à moteur 2018-04-30  jour  30 294904.2 5047549 -73.62678
## 3 Vol de véhicule à moteur 2018-09-01  nuit   7 290274.6 5042150 -73.68593
## 4                   Méfait 2017-07-21  jour  21      0.0       0 -76.23729
## 5                   Méfait 2017-07-29  jour  12      0.0       0 -76.23729
## 6 Vol de véhicule à moteur 2017-07-30  nuit  21      0.0       0 -76.23729
##   LATITUDE
## 1 45.56778
## 2 45.56778
## 3 45.51912
## 4  0.00000
## 5  0.00000
## 6  0.00000

Il semble que l’argument fileEncoding remporte! Nous pouvons donc réimporter les données comme il faut. Nous pourrions également downloader les données pour être sûr d’avoir une copie locale, advenant que le site disparait du Web.

dat <- read.csv(file = data_URL, fileEncoding = "Latin1")
# download.file(url = data_URL, destfile = 'copie_locale.csv')

Exploration

Maintenant que le jeu de données est importé, regardons voir si on comprend bien comment il est structuré.

dim(dat)
## [1] 202441      8
summary(dat)
##   CATEGORIE             DATE              QUART                PDQ      
##  Length:202441      Length:202441      Length:202441      Min.   : 1.0  
##  Class :character   Class :character   Class :character   1st Qu.:15.0  
##  Mode  :character   Mode  :character   Mode  :character   Median :26.0  
##                                                           Mean   :26.4  
##                                                           3rd Qu.:39.0  
##                                                           Max.   :55.0  
##                                                           NA's   :4     
##        X                Y             LONGITUDE         LATITUDE    
##  Min.   :     0   Min.   :      0   Min.   :-76.24   Min.   : 0.00  
##  1st Qu.:288271   1st Qu.:5035102   1st Qu.:-73.71   1st Qu.:45.46  
##  Median :295887   Median :5041400   Median :-73.61   Median :45.51  
##  Mean   :244829   Mean   :4173825   Mean   :-74.07   Mean   :37.68  
##  3rd Qu.:299207   3rd Qu.:5045887   3rd Qu.:-73.57   3rd Qu.:45.55  
##  Max.   :306390   Max.   :5062496   Max.   :-73.48   Max.   :45.70  
## 

On remarque 202375 observations et 8 variables CATEGORIE, DATE, QUART, PDQ, X, Y, LONGITUDE, LATITUDE. Certaines variables sont plutôt difficile à savoir ce qu’elles représentent, principalement PDF, X et Y et qu’il semble y avoir des 0 et des valeurs manquantes pour d’autres. Heureusement, il y a un Dictionnaire de données dans les informations complémentaires.

Dictionnaire de données

CATEGORIE : Nature de l'événement. Liste de valeur :
    Introduction : introduction par effraction dans un établissement public ou une résidence privée, vol d’arme à feu dans une résidence
    Vol dans / sur véhicule à moteur : vol du contenu d’un véhicule à moteur (voiture, camion, motocyclette, etc.) ou d’une pièce de véhicule (roue, parechoc, etc.)
    Vol de véhicule à moteur : vol de voiture, camion, motocyclette, motoneige tracteur avec ou sans remorque, véhicule de construction ou de ferme, tout-terrain
    Méfait : Graffiti et dommage de biens religieux, de véhicule ou dommage général et tous autres types de méfaits
    Vol qualifié : Vol accompagné de violence de commerce, institution financière, personne, sac à main, véhicule blindé, véhicule, arme à feu, et tous autres types de vols qualifiés
    Infraction entraînant la mort : Meurtre au premier degré, meurtre au deuxième degré, homicide involontaire, infanticide, négligence criminelle, et tous autres types d’infractions entraînant la mort

DATE : Date du signalement de l'événement au SPVM au format AAAA-MM-JJ HH:mm:ss (note: la partie de l'heure n'est pas utilisée)

QUART : Moment de la journée du signalement de l'événement au SPVM. Liste de valeur :
    jour : Entre 8h01 et 16h
    soir : Entre 16h01 et minuit
    nuit : Entre 00h01 et 8h

PDQ : Numéro du poste de quartier couvrant le territoire où s'est passé l'événement. Le territoire couvert par chaque poste est disponible dans l'ensemble de données des limites de PDQ

X et Y : Position géospatiale selon la projection MTM8 (SRID 2950)
    La valeur 0 est utilisée lorsqu'aucune position géographique n'a été fournie lors de la saisie de l'information.

LAT et LONG: position géographique de l'événement après obfuscation à une intersection selon le référentiel géodésique WGS84.

La valeur 1 est utilisée lorsqu'aucune position géographique n'a été fournie lors de la saisie de l'information.

À partir de ces infos, il faudrait déjà se questionner à propos de ce que nous ferons des 0 et des 1 pour les données géographiques, car celles-ci causeront problèmes pour la visualisation.

Mais avant, assurons-nous que les valeurs représentent bien le type de données que nous avons. Il y a, entre autres, DATE qui semble n’être que des chaines de caractères et PDQ des nombres entiers alors qu’ils représentent les numéros des postes de quartier.

Au-delà de la chaine de caractères, certaines données sont, en réalité, des facteurs, un type de structure de données dédié pour les données catégo- rielles qui permet d’automatiser plusieurs traitements. Quelles colonnes selon vous représentent des facteurs dans cet ensemble de données?

dat$DATE <- as.Date(dat$DATE)

# 1ère façon de faire 'manuelle' pour convertir en facteurs
dat$PDQ <- as.factor(dat$PDQ)
dat$CATEGORIE <- as.factor(dat$CATEGORIE)
dat$QUART <- as.factor(dat$QUART)

# 2e façon de faire
vars <- c("CATEGORIE", "PDQ", "QUART")
dat[vars] <- lapply(dat[vars], as.factor)

summary(dat)
##                             CATEGORIE          DATE             QUART       
##  Infractions entrainant la mort  :  177   Min.   :2015-01-01   jour:102500  
##  Introduction                    :53039   1st Qu.:2016-06-21   nuit: 30853  
##  Méfait                          :45952   Median :2017-12-17   soir: 69088  
##  Vol dans / sur véhicule à moteur:60410   Mean   :2018-02-25                
##  Vol de véhicule à moteur        :31151   3rd Qu.:2019-10-18                
##  Vols qualifiés                  :11712   Max.   :2021-10-08                
##                                                                             
##       PDQ               X                Y             LONGITUDE     
##  38     : 14293   Min.   :     0   Min.   :      0   Min.   :-76.24  
##  21     : 11829   1st Qu.:288271   1st Qu.:5035102   1st Qu.:-73.71  
##  20     : 11249   Median :295887   Median :5041400   Median :-73.61  
##  48     :  9790   Mean   :244829   Mean   :4173825   Mean   :-74.07  
##  39     :  9185   3rd Qu.:299207   3rd Qu.:5045887   3rd Qu.:-73.57  
##  (Other):146091   Max.   :306390   Max.   :5062496   Max.   :-73.48  
##  NA's   :     4                                                      
##     LATITUDE    
##  Min.   : 0.00  
##  1st Qu.:45.46  
##  Median :45.51  
##  Mean   :37.68  
##  3rd Qu.:45.55  
##  Max.   :45.70  
## 

Exercices

Maintenant que nous avons ce qu’il nous faut, il serait intéressant de voir si la criminalité augmente ou diminue entre chaque année.

Est-ce la même tendance pour chaque catégorie de crimes?

# format(dat$DATE, '%Y')

dat$DATE_year <- format(dat$DATE, "%Y")
dat$DATE_month <- format(dat$DATE, "%m")
dat$DATE_month_year <- format(dat$DATE, c("Y%", "%m"))
library(ggplot2)

# Make xtables
freq_year <- as.data.frame(table(dat$CATEGORIE, dat$DATE_year, useNA = "ifany"))
colnames(freq_year) <- c("CATEGORIE", "YEAR", "Freq")

freq_month_year <- as.data.frame(table(dat$CATEGORIE, dat$DATE_year, dat$DATE_month,
    useNA = "ifany"))
colnames(freq_month_year) <- c("CATEGORIE", "YEAR", "MONTH", "Freq")

ggplot(freq_year, aes(x = YEAR, y = Freq, group = CATEGORIE, color = CATEGORIE)) +
    geom_line(size = 1.5) + theme_bw() + labs(title = "Évolution annuelle de la criminalité rapportée depuis 2015 à Montréal",
    x = "Années", y = "Nombre de crimes") + theme(text = element_text(size = 16),
    axis.text.x = element_text(angle = 45, hjust = 0.5, vjust = 0.5), legend.position = "bottom") +
    geom_point(size = 3)

ggplot(freq_month_year, aes(x = MONTH, y = Freq, group = CATEGORIE, color = CATEGORIE)) +
    geom_line(size = 1.5) + facet_wrap(facets = vars(YEAR)) + geom_line(size = 1.5) +
    theme_bw() + labs(title = "Évolution annuelle de la criminalité rapportée depuis 2015 à Montréal",
    x = "Années", y = "Nombre de crimes") + theme(text = element_text(size = 16),
    axis.text.x = element_text(angle = 45, hjust = 0.5, vjust = 0.5), legend.position = "bottom") +
    geom_point(size = 3)

ggplot(freq_month_year, aes(x = YEAR, y = Freq, group = CATEGORIE, color = CATEGORIE)) +
    geom_line(size = 1.5) + facet_wrap(facets = vars(MONTH)) + geom_line(size = 1.5) +
    theme_bw() + labs(title = "Évolution annuelle de la criminalité rapportée depuis 2015 à Montréal",
    x = "Années", y = "Nombre de crimes") + theme(text = element_text(size = 16),
    axis.text.x = element_text(angle = 45, hjust = 0.5, vjust = 0.5), legend.position = "bottom") +
    geom_point(size = 3)

Et une map peut-être?

# install.packages('leaflet')
library(leaflet)

m <- leaflet(options = leafletOptions(minZoom = 10, maxZoom = 18))
m <- addTiles(m)
m <- setView(m, lng = -73.58781, lat = 45.50884, zoom = 11)
m <- addProviderTiles(m, providers$CartoDB.DarkMatter)

dat$COLOR <- sapply(dat$CATEGORIE, function(CATEGORIE) {
    if (CATEGORIE == "Infractions entrainant la mort") {
        "red"
    } else if (CATEGORIE == "Introduction") {
        "pink"
    } else if (CATEGORIE == "Méfait") {
        "orange"
    } else if (CATEGORIE == "Vol dans / sur véhicule à moteur") {
        "green"
    } else if (CATEGORIE == "Vol de véhicule à moteur") {
        "blue"
    } else {
        "black"
    }
})

icons <- awesomeIcons(icon = "ios-close", iconColor = "black", library = "ion", markerColor = dat$COLOR)

m <- addAwesomeMarkers(m, lng = dat$LONGITUDE, lat = dat$LATITUDE, icon = icons,
    label = paste(dat$QUART, "-", dat$CATEGORIE), clusterOptions = markerClusterOptions())

m